#ifndef tinynet_h__
#define tinynet_h__

#include "../../external/pstdint.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef struct 
{
	intptr_t socket;
	int state;
} tn_listener;

enum tn_listener_state
{
	TNL_NOT_CREATED = 0,
	TNL_LISTENING,
};

typedef struct 
{
	uint32_t ip;
	uint16_t port;
} tn_address;

struct tn_connection_internals;

typedef struct
{
	int state;
	uintptr_t socket;
	uint32_t remote_ip;
	struct tn_connection_internals* internals;
} tn_connection;

enum tn_connection_state
{
	TNC_COULD_NOT_CONNECT = -1, /// Could not connect to target host
	TNC_NOT_CREATED = 0, /// Connection is not created yet
	TNC_CONNECTING, /// Trying to connect to remote host
	TNC_ESTABLISHING, /// Establishing connection (determining whether both sides have the same protocol, etc)
	TNC_WORKING, /// Connection established, ready and working (sending and receiving data)
	TNC_DISCONNECTED, /// Connection terminated, you can (and have to) destroy it now
};

typedef struct
{
	char* data;
	uint16_t size;
	uint32_t internal_flags;
} tn_packet;

enum tn_errors
{
	TNE_OK = 0,
	TNE_UNKNOWN_ERROR,
	TNE_NOT_INITIALIZED,
	TNE_INCORRECT_PARAMETER,
	TNE_OUT_OF_MEMORY,
	TNE_NO_MORE_SOCKETS,
	TNE_CANNOT_REBIND_PORT,
	TNE_LISTENER_NOT_ACTIVE,
	TNE_ALREADY_CONNECTED,
	TNE_NOT_CONNECTED,
	TNE_NOT_CREATED,
	TNE_WRONG_NETWORK_PROTOCOL,
};

/// TODO: regular pinging?

int tn_init(void);
void tn_shutdown(void);

int tn_get_last_error(void);

int tn_set_address(tn_address* dest, const char* host, uint16_t port);
void tn_print_address(tn_address* address, char* buffer);
void tn_print_ip(uint32_t ip, char* buffer);

tn_listener* tn_create_listener(tn_address* address);
void tn_destroy_listener(tn_listener* listener);

tn_connection* tn_connect_to(tn_address* address);

tn_connection* tn_listen(tn_listener* listener);
void tn_destroy_connection(tn_connection* connection);

/*
  The data in the packet returned by this function will be destroyed along with the packet.
  To make sure that does not happen, use the `tn_release_packet_data` function. Use free()
  to delete the data after you are finished with it.
*/
tn_packet* tn_work_connection(tn_connection* connection);
void tn_destroy_packet(tn_packet* packet);

/// Returns 0 on success, -1 on error
int tn_send_data(tn_connection* connection, void* data, uint16_t size);
int tn_send_packet(tn_connection* connection, tn_packet* packet);
int tn_send_string(tn_connection* connection, const char* string);
#define tn_send_struct(conn, obj) tn_send_data(conn, &obj, sizeof(obj))

///* Releases the packet data, so that it won't be destroyed after destroying the packet */
char* tn_release_packet_data(tn_packet* packet);

#ifdef __cplusplus
};
#endif

#endif // tinynet_h__